/** @file libsvm_dataset.cc
 *
 *  The class for supporting the LibSVM format.
 *
 *  @author Dongryeol Lee (drselee@gmail.com)
 */

#include "core/data/sparse_dataset.h"
#include "core/data/libsvm_parser.h"

namespace core {
namespace data {

void SparseDataset::as_dense_matrix(arma::mat *dense_matrix_out) const {
  dense_matrix_out->zeros(
    this->num_attributes(), col_collections_.size());
  for(unsigned int j = 0; j < col_collections_.size(); j++) {
    for(int i = 0; i < this->num_attributes(); i++) {
      dense_matrix_out->at(i, j) =
        (*(col_collections_[ j ]))[i].operator double();
    }
  }
}

SparseDataset::~SparseDataset() {
  for(unsigned int i = 0; i < col_collections_.size(); i++) {
    delete col_collections_[i];
  }
}

double SparseDataset::average_num_points() const {
  return average_num_points_;
}

arma::SpCol<double> &SparseDataset::get_instance(int point_id) {
  return *(col_collections_[ point_id ]);
}

const arma::SpCol<double> &SparseDataset::get_instance(int point_id) const {
  return *(col_collections_[ point_id ]);
}

core::data::alias_sparse_vec &SparseDataset::get_instance(
  int point_id, core::data::alias_sparse_vec **targets_out) {
  * targets_out =  raw_target_collections_[ point_id ] ;
  return * (raw_point_collections_[ point_id]);
}

const core::data::alias_sparse_vec &SparseDataset::get_instance(
  int point_id,
  const core::data::alias_sparse_vec **targets_out) const {
  *targets_out = raw_target_collections_[point_id];
  return * (raw_point_collections_[ point_id]);
}

void SparseDataset::Init(int num_attributes_in, int num_points_in) {

  // Set up in a default manner.
  this->SetupDefaultAttributeList_(num_attributes_in);

  // Initialize and setup pointers.
  col_collections_.resize(num_points_in);
  raw_point_collections_.resize(num_points_in);
  raw_target_collections_.resize(num_points_in);
  for(int i = 0; i < num_points_in; i++) {
    col_collections_[i] = new arma::SpCol<double>(num_attributes_in, 1);
    raw_point_collections_[i] =
      new core::data::alias_sparse_vec(
      * col_collections_[i], raw_point_attribute_indices_);
    raw_target_collections_[i] =
      new core::data::alias_sparse_vec(
      * col_collections_[i], raw_target_attribute_indices_);
  }

  // Set the avearge number of points.
  average_num_points_ = num_points_in;
}

void SparseDataset::Init(
  boost::mpi::communicator &world,
  const std::string &file_name_in, bool from_single_file) {

  if(! from_single_file) {

    // Treat the file name as a prefix.
    std::stringstream file_name_sstr;
    file_name_sstr << file_name_in << world.rank();
    std::string file_name(file_name_sstr.str());
    core::data::LibSVMParser::Parse(
      &world, file_name, this, 0, std::numeric_limits<int>::max());
  }
  else {

    // The received partition.
    std::pair< int, int > received_partition;

    // The master partitions and sends each partition to each process.
    if(world.rank() == 0) {
      std::ifstream is(file_name_in.c_str(), std::ios_base::binary);
      is.seekg(0, std::ios::end);
      int length = is.tellg();
      is.clear();
      is.seekg(0, std::ios::beg);

      // Partition of the text file.
      std::vector < std::pair<int, int> > partition_list;
      FindPartition_(length, world.size(), is, false, & partition_list);
      boost::mpi::scatter(world, partition_list, received_partition, 0);
    }
    else {
      boost::mpi::scatter(world, received_partition, 0);
    }

    // Perform parallel parsing.
    core::data::LibSVMParser::Parse(
      &world, file_name_in, this,
      received_partition.first, received_partition.second);
  }

  // Figure out the average number of points across all MPI processes.
  boost::mpi::all_reduce(
    world, static_cast<double>(this->num_points()),
    average_num_points_, std::plus<double>());
  average_num_points_ /= static_cast<double>(world.size());
}

bool SparseDataset::Init(const std::string &file_name_in) {
  core::data::LibSVMParser::Parse(
    NULL, file_name_in, this, -1, std::numeric_limits<int>::max());
  average_num_points_ = this->num_points();
  return true;
}

int SparseDataset::num_points() const {
  return col_collections_.size();
}

void SparseDataset::RandomGenerate(
  boost::mpi::communicator &world, int num_dimensions, int num_points)  {
}
}
}
